package com.zx.framework.core.util;

import org.apache.commons.codec.binary.Base64;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.util.Random;

/**
 *  验证码生成类
 */
public class CaptchaUtil {
    private static Logger LOG = LoggerFactory.getLogger(CaptchaUtil.class);
	private int width = 102;
	private int height = 28;
	private int codeCount = 4;

	private Random random = new Random();

    /**
     * 波状扭曲随机量
     */
    private int waveRan = 7;
    /**
     * 波状扭曲基础值
     */
    private int waveBase = 3;
    /**
     * 波状扭曲相位偏移
     */
    private int wavePhase = 6;

    /**
     * 扩散扭曲倍率
     */
    private int twistMulti= 2;
    /**
     * 扩散扭曲基础值
     */
    private int twistBase = 1;

    /**
     * 图片旋转范围
     */
    private int rotate = 22;

	/**
	 * 验证码图片
	 */
	private BufferedImage buffImg;
	/**
	 * 验证码字符串
	 */
	private String checkCodeStr;
    public CaptchaUtil(){
    }
    public CaptchaUtil(int waveRan,int waveBase,int wavePhase,int twistMulti,int twistBase,int rotate){
        this.waveRan = waveRan;
        this.waveBase = waveBase;
        this.wavePhase = wavePhase;
        this.twistMulti = twistMulti;
        this.twistBase = twistBase;
        this.rotate = rotate;
    }

    public static CaptchaUtil createCheckCode(int waveRan,int waveBase,int wavePhase,int twistMulti,int twistBase,int rotate){
        CaptchaUtil checkCode = new CaptchaUtil(waveRan,waveBase,wavePhase,twistMulti,twistBase,rotate);
        checkCode.setCheckCodeStr(checkCode.createRandomCode());
        checkCode.setBuffImg(checkCode.disturb());
        return checkCode;
    }
	/**
	 * 创建验证码对象
	 * @return CaptchaUtil
	 */
	public static CaptchaUtil createCheckCode() {
		CaptchaUtil checkCode = new CaptchaUtil();
		checkCode.setCheckCodeStr(checkCode.createRandomCode());
		checkCode.setBuffImg(checkCode.disturb());
		return checkCode;
	}

    /**
     * 使用指定的参数创建验证码对象
     * @param width     宽度
     * @param height    高度
     * @param codeCount 验证码字数
     * @return  CaptchaUtil
     */
    public static CaptchaUtil createCheckCode(int width,int height,int codeCount) {
        CaptchaUtil checkCode = new CaptchaUtil();
        checkCode.setWidth(width);
        checkCode.setHeight(height);
        checkCode.setCodeCount(codeCount);
        checkCode.setCheckCodeStr(checkCode.createRandomCode());
        checkCode.setBuffImg(checkCode.disturb());
        return checkCode;
    }

	/**
	 * 随机产生的验证码
	 * @return String
	 */
	private String createRandomCode() {
		StringBuilder randomCode = new StringBuilder();

		String strRand;
		int xx = (width-15) / (codeCount + 1);
		int codeY = height - 4;
		char[] codeSequence = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J',
				'K', 'L', 'M', 'N', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
				'X', 'Y', 'Z', '2', '3', '4', '5', '6', '7', '8', '9' };

		Graphics2D graphics = graphicsInit();
		

		for (int i = 0; i < codeCount; i++) {
			strRand = String.valueOf(codeSequence[random.nextInt(32)]);
			randomCode.append(strRand);
			
			//随机取色
			graphics.setColor(createColor());
			
			//想文字旋转一定的角度  
            AffineTransform trans = new AffineTransform();  
            trans.rotate((rotate-random.nextInt(2*rotate))*3.14/180, (i + 1) * xx, codeY) ;
            //缩放文字  
            float scaleSize = random.nextFloat() + 0.8f ;  
            if(scaleSize>1f)  
                scaleSize = 1f ;  
            trans.scale(scaleSize, scaleSize) ;
            
            graphics.setTransform(trans);
			graphics.drawString(strRand, (i + 1) * xx, codeY);
		}

		return randomCode.toString();
	}

	/**
	 * 创建颜色
	 * @return Color
	 */
	private Color createColor() {
		Color color[] = new Color[10];
		color[0] = new Color(113, 31, 71);
		color[1] = new Color(37, 0, 37);
		color[2] = new Color(111, 33, 36);
		color[3] = new Color(0, 0, 112);
		color[4] = new Color(14, 51, 16);
		color[5] = new Color(1, 1, 1);
		color[6] = new Color(72, 14, 73);
		color[7] = new Color(65, 67, 29);
		color[8] = new Color(116, 86, 88);
		color[9] = new Color(41, 75, 71);

		return color[random.nextInt(10)];
	}

	/**
	 * 绘制类初始化
	 * @return Graphics2D
	 */
	private Graphics2D graphicsInit() {
		Graphics2D graphics = buffImgInit().createGraphics();
		graphics.setColor(Color.WHITE);
		graphics.fillRect(0, 0, width, height);
		graphics.setFont(new Font("Fixedsys", Font.BOLD, height - 2));
		graphics.drawRect(0, 0, width - 1, height - 1);
		return graphics;
	}

	/**
	 * BufferedImage初始化
	 * @return BufferedImage
	 */
	private BufferedImage buffImgInit() {
		buffImg = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
		return buffImg;
	}

	/**
	 * 绘制干扰特性
	 * @return BufferedImage
	 */
	private BufferedImage disturb() {
		drawDisturbLine(buffImg.createGraphics());
		return twistImage();
	}

	/**
	 * 画干扰线使图象中的认证码不易被其它程序探测到
	 * @param graphics 原图像文件
	 */
	private void drawDisturbLine(Graphics2D graphics) {
		graphics.setColor(createColor());
		int x,y,xl,yl;
		for (int i = 0; i < 15; i++) {
			x = random.nextInt(width);
			y = random.nextInt(height);
			xl = random.nextInt(20);
			yl = random.nextInt(10);
			graphics.drawLine(x, y, x + xl, y + yl);
		}
	}

	/**
	 * 正弦曲线Wave扭曲图片
	 * @return BufferedImage
	 */
	private BufferedImage twistImage() {
		double dMultValue = random.nextInt(waveRan) + waveBase;  // 波形的幅度倍数，越大扭曲的程序越高，一般为3-5
		double dPhase = random.nextInt(wavePhase);          // 波形的起始相位，取值区间（0-2＊PI）

		BufferedImage destBi = new BufferedImage(buffImg.getWidth(),
				buffImg.getHeight(), BufferedImage.TYPE_INT_RGB);

		for (int i = 0; i < destBi.getWidth(); i++) {
			for (int j = 0; j < destBi.getHeight(); j++) {
				int nOldX = getXPosition4Twist(dPhase, dMultValue,
						destBi.getHeight(), i, j);
				int nOldY = j;
//				int nOldY = getXPosition4Twist(dPhase, dMultValue,
//						destBi.getWidth(), j, i);
				nOldX = nOldX<0?destBi.getWidth()+nOldX:nOldX;
				nOldX = nOldX>=destBi.getWidth()?nOldX-destBi.getWidth():nOldX;
				nOldY = nOldY<0?destBi.getHeight()+nOldY:nOldY;
				nOldY = nOldY>=destBi.getHeight()?nOldY-destBi.getHeight():nOldY;
				destBi.setRGB(nOldX, nOldY, buffImg.getRGB(i, j));
				if(random.nextInt(100)<5){
					destBi.setRGB(i, j, 0);
				}
			}
		}
		return destBi;
	}

	/**
	 * 获取扭曲后的x轴位置
	 * @param dPhase        图片扭动相位
	 * @param dMultValue    横向扩散倍率
	 * @param height        高度
	 * @param xPosition     横向定位
	 * @param yPosition     纵向定位
	 * @return int
	 */
	private int getXPosition4Twist(double dPhase, double dMultValue,
			int height, int xPosition, int yPosition) {
		double PI = twistBase+random.nextInt(twistMulti); // 此值越大，扭曲程度越大
		double dx = (PI * yPosition) / height + dPhase;
		double dy = Math.sin(dx);
		return xPosition + (int) (dy * dMultValue);
	}

	/**
	 * 
	 * 将图像进行输出到文件
	 * @param pathName 输出目录
	 * @return String 输出目录
	 */
	public String createImgFile(String pathName) throws IOException {
		File file = new File(pathName);
		if (file.isFile() && file.exists()) {
            if(!file.delete()){
                LOG.warn("file delete error,file not found or could not be delete");
            }
		}
		try {
			ImageIO.write(buffImg, "jpeg", file);
		} catch (IOException e) {
            LOG.error("file write err!",e);
            throw new IOException(e);
		}
		return pathName;
	}

    public byte[] getImgByte() throws Exception {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        try {
            ImageIO.write(getBuffImg(), "jpeg", baos);
        } catch (IOException e) {
            LOG.error("验证码图片转换base64失败",e);
            throw new Exception(e);
        }
        return baos.toByteArray();
    }

	public BufferedImage getBuffImg() {
		return buffImg;
	}

	public void setBuffImg(BufferedImage buffImg) {
		this.buffImg = buffImg;
	}

	public String getCheckCodeStr() {
		return checkCodeStr;
	}

	public void setCheckCodeStr(String checkCodeStr) {
		this.checkCodeStr = checkCodeStr;
	}

    public String getImgBase64() throws Exception {
        return new String(Base64.encodeBase64(getImgByte()));
    }

	public int getWidth() {
		return width;
	}

	public void setWidth(int width) {
		this.width = width;
	}

	public int getHeight() {
		return height;
	}

	public void setHeight(int height) {
		this.height = height;
	}

	public int getCodeCount() {
		return codeCount;
	}

	public void setCodeCount(int codeCount) {
		this.codeCount = codeCount;
	}

    public int getWaveRan() {
        return waveRan;
    }

    public void setWaveRan(int waveRan) {
        this.waveRan = waveRan;
    }

    public int getWaveBase() {
        return waveBase;
    }

    public void setWaveBase(int waveBase) {
        this.waveBase = waveBase;
    }

    public int getWavePhase() {
        return wavePhase;
    }

    public void setWavePhase(int wavePhase) {
        this.wavePhase = wavePhase;
    }

    public int getTwistMulti() {
        return twistMulti;
    }

    public void setTwistMulti(int twistMulti) {
        this.twistMulti = twistMulti;
    }

    public int getTwistBase() {
        return twistBase;
    }

    public void setTwistBase(int twistBase) {
        this.twistBase = twistBase;
    }

    public int getRotate() {
        return rotate;
    }

    public void setRotate(int rotate) {
        this.rotate = rotate;
    }
}
